package com.eva.admin.biz.cms.article;

import cn.hutool.core.bean.BeanUtil;
import com.eva.admin.dao.cms.CmsArticleMapper;
import com.eva.admin.biz.cms.article.vo.CmsArticleVO;
import com.eva.admin.biz.cms.article.dto.SaveCmsArticleDTO;
import com.eva.admin.biz.cms.article.dto.QueryCmsArticleDTO;
import com.eva.enums.CmsArticleStatusEnum;
import com.eva.enums.CmsCategoryTypeEnum;
import com.eva.dao.cms.model.CmsArticleTag;
import com.eva.dao.cms.model.CmsCategory;
import com.eva.framework.common.constants.ResponseStatus;
import com.eva.framework.common.exception.BusinessException;
import com.eva.framework.common.model.PageData;
import com.eva.framework.common.model.PageWrap;
import com.eva.framework.common.utils.AssertUtil;
import com.eva.dao.cms.model.CmsArticle;
import com.eva.framework.mp.utils.PageUtil;
import com.eva.framework.rbac.model.RbacUserInfo;
import com.eva.framework.rbac.session.SessionUtil;
import com.eva.service.cms.CmsArticleService;
import com.eva.service.cms.CmsArticleTagService;
import com.eva.service.cms.CmsCategoryService;
import com.eva.service.common.Generator;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class CmsArticleBiz {

    @Resource
    private CmsArticleMapper cmsArticleMapper;

    @Resource
    private CmsArticleService cmsArticleService;

    @Resource
    private CmsCategoryService cmsCategoryService;

    @Resource
    private CmsArticleTagService cmsArticleTagService;

    @Resource
    private Generator generator;

    /**
     * 发布文章
     *
     * @param id 文章ID
     */
    public void publish (Integer id) {
        // 基础验证
        AssertUtil.notNull(id, "缺少文章ID");
        CmsArticle target = cmsArticleService.findById(id);
        AssertUtil.notEmpty(target, "未找到对应文章，请刷新后重试！");
        AssertUtil.notEmpty(target.getTitle(), "文章缺少标题！");
        AssertUtil.notEmpty(target.getCategoryIds(), "文章至少需要指定一个栏目！");
        AssertUtil.notEmpty(target.getUid(), "文章唯一标识不能为空！");
        AssertUtil.notEmpty(target.getContentDigest(), "文章缺少摘要信息！");
        // 状态验证
        AssertUtil.notEquals(target.getStatus(), CmsArticleStatusEnum.ONLINE.name(), "文章已发布，请勿重复发布！");
        // 越权验证
        this.checkPrivilege(target);
        // 发布
        CmsArticle newRecord = new CmsArticle();
        newRecord.setId(id);
        newRecord.setStatus(CmsArticleStatusEnum.ONLINE.name());
        cmsArticleService.updateById(newRecord);
    }

    /**
     * 上线
     *
     * @param id 文章ID
     */
    public void online (Integer id) {
        // 基础验证
        AssertUtil.notNull(id, "缺少文章ID");
        CmsArticle target = cmsArticleService.findById(id);
        AssertUtil.notEmpty(target, "未找到对应文章，请刷新后重试！");
        // 状态验证
        AssertUtil.equals(target.getStatus(), CmsArticleStatusEnum.OFFLINE.name(), "文章状态不可上线！");
        // 越权验证
        this.checkPrivilege(target);
        // 上线
        CmsArticle newRecord = new CmsArticle();
        newRecord.setId(id);
        newRecord.setStatus(CmsArticleStatusEnum.ONLINE.name());
        cmsArticleService.updateById(newRecord);
    }

    /**
     * 批量上线
     *
     * @param ids 文章ID集
     * @return 成功上线数
     */
    public int onlineInBatch (List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return 0;
        }
        int count = 0;
        for (Integer id : ids) {
            try {
                this.online(id);
                count ++;
            } catch (BusinessException ignore) {}
        }
        return count;
    }

    /**
     * 下线文章
     *
     * @param id 文章ID
     */
    public void offline (Integer id) {
        // 基础验证
        AssertUtil.notNull(id, "缺少文章ID");
        CmsArticle target = cmsArticleService.findById(id);
        AssertUtil.notEmpty(target, "未找到对应文章，请刷新后重试！");
        // 状态验证
        AssertUtil.equals(target.getStatus(), CmsArticleStatusEnum.ONLINE.name(), "文章未上线！");
        // 越权验证
        this.checkPrivilege(target);
        // 下线
        CmsArticle newRecord = new CmsArticle();
        newRecord.setId(id);
        newRecord.setStatus(CmsArticleStatusEnum.OFFLINE.name());
        cmsArticleService.updateById(newRecord);
    }

    /**
     * 批量下线
     *
     * @param ids 文章ID集
     * @return 成功下线数
     */
    public int offlineInBatch (List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return 0;
        }
        int count = 0;
        for (Integer id : ids) {
            try {
                this.offline(id);
                count ++;
            } catch (BusinessException ignore) {}
        }
        return count;
    }

    /**
     * 保存草稿
     *
     * @param dto 创建参数
     */
    @Transactional
    public CmsArticle save(SaveCmsArticleDTO dto) {
        // 基础验证
        AssertUtil.notEmpty(dto.getTitle(), "文章标题不能为空");
        // 对新栏目做越权验证（只允许指定自己拥有的栏目）
        if (StringUtils.isNotBlank(dto.getCategoryIds())) {
            // 获取文章最新的所属栏目
            Set<Integer> categoryIds = Arrays.stream(dto.getCategoryIds().split(","))
                    .filter(StringUtils::isNotBlank)
                    .map(Integer::valueOf)
                    .collect(Collectors.toSet());
            // 执行越权验证
            this.checkCategoryPrivilege(categoryIds);
        }
        // 存在ID，保存最新数据
        if (dto.getId() != null) {
            // 查询文章
            CmsArticle target = cmsArticleService.findById(dto.getId());
            AssertUtil.notEmpty(target, "未找到对应文章，请刷新后重试！");
            // 补充和验证文章唯一标识
            if (StringUtils.isBlank(dto.getUid())) {
                dto.setUid(generator.generateArticleUid());
            } else {
                if (cmsArticleService.exists(CmsArticle.builder().id(target.getId()).uid(dto.getUid()).build())) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST, "文章唯一标识已存在！");
                }
            }
            // 对原本文章的栏目做越权验证（防止恶意用户篡改文章数据）
            this.checkPrivilege(target);
            // 构建对象
            CmsArticle newRecord = new CmsArticle();
            BeanUtils.copyProperties(dto, newRecord);
            newRecord.setId(dto.getId());
            // 执行修改
            cmsArticleService.updateById(newRecord);
            return newRecord;
        }
        // 不存在ID，创建新的文章
        CmsArticle newRecord = new CmsArticle();
        BeanUtils.copyProperties(dto, newRecord);
        newRecord.setStatus(CmsArticleStatusEnum.DRAFT.name());
        // - 补充和验证文章标识
        if (StringUtils.isBlank(dto.getUid())) {
            newRecord.setUid(generator.generateArticleUid());
        } else {
            if (cmsArticleService.exists(CmsArticle.builder().uid(dto.getUid()).build())) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST, "文章唯一标识已存在！");
            }
        }
        cmsArticleService.create(newRecord);
        return newRecord;
    }

    /**
     * 根据主键删除
     *
     * @param id 主键
     */
    @Transactional
    public void deleteById(Integer id) {
        // 基础验证
        AssertUtil.notEmpty(id, "主键不能为空");
        CmsArticle target = cmsArticleService.findById(id);
        AssertUtil.notEmpty(target, "未找到对应文章，请刷新后重试！");
        // 状态验证，已上线的文章不可删除
        if (target.getStatus().equals(CmsArticleStatusEnum.ONLINE.name())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST, "文章已发布，请下线后再删除！");
        }
        // 越权验证
        this.checkPrivilege(target);
        // 执行删除
        cmsArticleService.deleteById(id);
    }

    /**
     * 根据主键批量删除
     *
     * @param ids 主键集合
     */
    @Transactional
    public Integer deleteByIdInBatch(List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return 0;
        }
        int count = 0;
        for (Integer id : ids) {
            try {
                this.deleteById(id);
                count ++;
            } catch (Exception ignore) {}
        }
        return count;
    }

    /**
     * 分页查询
     *
     * @param pageWrap 分页参数
     * @return 分页数据
     */
    public PageData<CmsArticleVO> findPage(PageWrap<QueryCmsArticleDTO> pageWrap) {
        PageHelper.startPage(pageWrap.getPage(), pageWrap.getCapacity());
        List<CmsArticleVO> result = cmsArticleMapper.search(pageWrap.getModel());
        for (CmsArticleVO article : result) {
            // 补充所属栏目
            article.setCategories(Collections.emptyList());
            if (StringUtils.isNotBlank(article.getCategoryIds())) {
                Set<Integer> categoryIds = Arrays.stream(article.getCategoryIds().split(","))
                        .filter(StringUtils::isNotBlank)
                        .map(Integer::valueOf)
                        .collect(Collectors.toSet());
                if (!CollectionUtils.isEmpty(categoryIds)) {
                    List<CmsCategory> categories = cmsCategoryService.findByIds(categoryIds);
                    article.setCategories(BeanUtil.copyToList(categories, CmsArticleVO.Category.class));
                }
            }
            // 补充标签集
            article.setTags(Collections.emptyList());
            if (StringUtils.isNotBlank(article.getTagIds())) {
                Set<Integer> tagIds = Arrays.stream(article.getTagIds().split(","))
                        .filter(StringUtils::isNotBlank)
                        .map(Integer::valueOf)
                        .collect(Collectors.toSet());
                if (!CollectionUtils.isEmpty(tagIds)) {
                    List<CmsArticleTag> tags = cmsArticleTagService.findByIds(tagIds);
                    article.setTags(BeanUtil.copyToList(tags, CmsArticleVO.Tag.class));
                }
            }
        }
        return PageUtil.from(new PageInfo<>(result));
    }

    /**
     * 验证文章操作是否存在越权
     *
     * @param article 文章
     */
    private void checkPrivilege(CmsArticle article) {
        if (StringUtils.isBlank(article.getCategoryIds())) {
            return;
        }
        // 获取文章所属栏目
        Set<Integer> categoryIds = Arrays.stream(article.getCategoryIds().split(","))
                .filter(StringUtils::isNotBlank)
                .map(Integer::valueOf)
                .collect(Collectors.toSet());
        // 执行越权验证
        this.checkCategoryPrivilege(categoryIds);
    }

    /**
     * 验证栏目越权
     *
     * @param categoryIds 栏目ID集
     */
    private void checkCategoryPrivilege(Set<Integer> categoryIds) {
        // 不存在栏目，视为不存在越权行为
        if (categoryIds.isEmpty()) {
            return;
        }
        // 获取栏目信息
        List<CmsCategory> targetCategories = cmsCategoryService.findByIds(categoryIds);
        // 验证栏目类型是否均为常规栏目
        if (!targetCategories.stream().allMatch(category -> CmsCategoryTypeEnum.DEFAULT.name().equals(category.getType()))) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED, "文章不允许挂载到当前栏目中！");
        }
        // 获取到栏目的权限ID集合
        Set<Integer> targetCategoryPermissionIds = targetCategories.stream()
                .map(CmsCategory::getPermissionId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        // 目标栏目均无需权限，视为不存在越权行为
        if (targetCategoryPermissionIds.isEmpty()) {
            return;
        }
        // 验证登录用户是否拥有任意一个栏目的权限
        RbacUserInfo loginUserInfo = SessionUtil.getLoginUser();
        // - 未配置权限的超管用户，视为拥有所有权限
        if (loginUserInfo.getIsSuperAdmin() && loginUserInfo.getCategoryPermissionIds().isEmpty()) {
            return;
        }
        if (!loginUserInfo.getCategoryPermissionIds().retainAll(targetCategoryPermissionIds)) {
            throw new BusinessException(ResponseStatus.PRIVILEGE_ERROR);
        }
    }
}
